﻿using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Formatting;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic;
using System.Linq;

namespace JsonCodeGenerator
{
    public class GeneratorService
    {
        /// <summary>
        /// json格式化
        /// </summary>
        public static string JsonFormate(string json)
        {
            try
            {
                var token = JToken.Parse(json);

                return token.ToString(Formatting.Indented);
            }
            catch (Exception ex)
            {
                return "Json字符串格式有问题\n" + ex.Message;
            }
        }

        /// <summary>
        /// 代码生成
        /// </summary>
        public static string CodeGenerator(string json, GeneratorConfig config)
        {
            try
            {
                var syntaxTree = BuildSyntaxTree(json);

                var rootNode = syntaxTree.GetRoot();

                if (config.IsJsonPropertyAttribute)
                {
                    var rewriter = new AttributeRewriter("JsonProperty(\"{0}\")");
                    rootNode = rewriter.Visit(rootNode);
                }

                if (config.IsJsonPropertyNameAttribute)
                {
                    var rewriter = new AttributeRewriter("JsonPropertyName(\"{0}\")");
                    rootNode = rewriter.Visit(rootNode);
                }

                if (config.IsXmlComment)
                {
                    var commentRewriter = new XmlCommentRewriter();
                    rootNode = commentRewriter.Visit(rootNode);
                }

                if (config.IsTitleCase)
                {
                    var walker = new GetPropertyWalker();
                    walker.Visit(rootNode);

                    foreach (var propertyName in walker.PropertyNames)
                    {
                        var PNRewriter = new PropertyNameRewriter(propertyName, propertyName.ToUppercase());
                        rootNode = PNRewriter.Visit(rootNode);
                    }
                }

                if (config.IsToCamelCase)
                {
                    var walker = new GetPropertyWalker();
                    walker.Visit(rootNode);

                    foreach (var propertyName in walker.PropertyNames)
                    {
                        var PNRewriter = new PropertyNameRewriter(propertyName, propertyName.ConvertToCamelCase());
                        rootNode = PNRewriter.Visit(rootNode);
                    }
                }

                var formattedRoot = Formatter.Format(rootNode, new AdhocWorkspace());
                return formattedRoot.ToFullString();
            }
            catch (Exception ex)
            {
                return "Json字符串格式有问题\n" + ex.Message;
            }
        }

        public static SyntaxTree BuildSyntaxTree(string json, string className = "")
        {
            var chidrenCodes = new List<string>();
            className = string.IsNullOrEmpty(className) ? "DynamicObject" : className;
            var code = $"public class {className} {{";

            var jsonObj = JObject.Parse(json);

            foreach (var prop in jsonObj.Properties())
            {
                if (prop.Value is JObject nestedObj)
                {
                    code += $"public {prop.Name.ToUppercase().ConvertToCamelCase()} {prop.Name} {{ get; set; }}\n\n";

                    var nestedSyntaxTree = BuildSyntaxTree(nestedObj.ToString(), prop.Name.ToUppercase().ConvertToCamelCase());
                    //chidrenCodes.Add(nestedSyntaxTree.GetCompilationUnitRoot().Members.First().ToString());
                    chidrenCodes.Add(nestedSyntaxTree.GetRoot().ToFullString());
                }
                else if (prop.Value is JArray nesttedArray)
                {
                    code += $"public List<{prop.Name.ToUppercase().ConvertToCamelCase()}> {prop.Name} {{ get; set; }}\n\n";

                    if (nesttedArray.Any(m => m is JValue v && v.Type == JTokenType.String))
                    {
                        var propType = prop.Value.Type.ToString().ToLower();
                        code += $"public List<string> {prop.Name} {{ get; set; }}\n\n";
                    }
                    else if (nesttedArray.Any(m => m is JValue v && v.Type == JTokenType.Integer))
                    {
                        var propType = prop.Value.Type.ToString().ToLower();
                        code += $"public List<int> {prop.Name} {{ get; set; }}\n\n";
                    }
                    else
                    {
                        var arrayObject = nesttedArray.Select(m => m as JObject).MaxBy(m => m!.Properties().Count());
                        var nestedSyntaxTree = BuildSyntaxTree(arrayObject!.ToString(), prop.Name.ToUppercase().ConvertToCamelCase());
                        //chidrenCodes.Add(nestedSyntaxTree.GetCompilationUnitRoot().Members.First().ToString());
                        chidrenCodes.Add(nestedSyntaxTree.GetRoot().ToFullString());
                    }
                }
                else
                {
                    var propType = prop.Value.Type.ToString().ToLower();

                    if (prop.Value.Type == JTokenType.Integer)
                    {
                        propType = "int";
                    }

                    if (prop.Value.Type == JTokenType.Null)
                    {
                        propType = "string";
                    }

                    code += $"public {propType} {prop.Name} {{ get; set; }}\n\n";
                }
            }

            code += "}";

            chidrenCodes.ForEach(m => code = code + "\n" + m);

            return CSharpSyntaxTree.ParseText(code);
        }
    }
}
